// heapMgr.cpp
//
//    Simple heap manager to reserve heap memory for low memory conditions

#include "heapMgr.h"
#include "debugstream.h"
#include <iostream>

namespace simple{
    //
    // HeapMgr
    //
    HeapMgr* HeapMgr::_sOnly = 0;
    HeapMgr& HeapMgr::gOnly()
    {
        if (!_sOnly) _sOnly = new HeapMgr ();
        return *_sOnly;
    }

    HeapMgr::HeapMgr () 
        : _oldHandler (0), _reserve (0), _size (0),
        _state (true), _nested (false)
    {}


    void HeapMgr::releaseMemory ()
    {
        if (_reserve) {
            delete [] _reserve;
            _reserve = 0;
            cdebug << "HeapMgr::releaseMemory() has released "
                   << _size << " bytes" << std::endl;
            _size = 0;
        }
    }

    void HeapMgr::cleanup ()
    {
        releaseMemory ();

        if (_oldHandler) {
            std::set_new_handler (_oldHandler);
            _oldHandler = 0;
        }
    }


    int HeapMgr::allocate (int desiredSize)
    {
        cleanup ();

        while (_reserve == 0 && desiredSize > 0) {
            try {
                _reserve = new char [desiredSize];
                _size = desiredSize;
            }
            catch (std::bad_alloc&) {
                desiredSize /= 2;
            }
        }

        // Install our handler
        if (_reserve)
            _oldHandler = std::set_new_handler (newHandler);

        cdebug << "HeapMgr::allocate() has reserved "
               << _size << " bytes" << std::endl;

        return desiredSize;
    }

    // newHandler() is called when heap cannot be allocated
    void HeapMgr::newHandler ()
    {
        if (HeapMgr::gOnly()._nested) {
            // We have recursed into our handler which is a catastrophic bug
            // We cannot generate a message because this is how we probably
            // got here in the first place.
            throw std::bad_alloc ();
        }

        HeapMgr::gOnly()._nested = true;

        if (HeapMgr::gOnly()._state) {
            // Free our memory if we have not already done so
            if (HeapMgr::gOnly()._reserve) {
                HeapMgr::gOnly().releaseMemory ();
                HeapMgr::gOnly()._nested = false;
                return;
            }
        }
     
        cdebug << "Throwing bad_alloc from newHandler" << std::endl;
        HeapMgr::gOnly()._nested = false;
        throw std::bad_alloc ();
    }


}